#ifndef CS488_VIEWER_HPP
#define CS488_VIEWER_HPP

#include <gtkmm.h>
#include <gtkglmm.h>
#include "algebra.hpp"
#include "a2.hpp"

// The "main" OpenGL widget
class Viewer : public Gtk::GL::DrawingArea {
public:
  Viewer();
  virtual ~Viewer();
	enum myMode{
		MODELROTATE,
		MODELTRANSLATE,
		MODELSCALE,
		VIEWROTATE,
		VIEWTRANSLATE,
		VIEWPERSPECTIVE,
		VIEWPORT
	};
  // A useful function that forces this widget to rerender. If you
  // want to render a new frame, do not call on_expose_event
  // directly. Instead call this, which will cause an on_expose_event
  // call when the time is right.
  void invalidate();

  // *** Fill in these functions (in viewer.cpp) ***

  // Set the parameters of the current perspective projection using
  // the semantics of gluPerspective().
  void set_perspective(double fov, double aspect,
                       double near, double far);

  // Restore all the transforms and perspective parameters to their
  // original state. Set the viewport to its initial size.
  void reset_view();

	// user interface calls
	void set_mode(myMode m);
	void set_label(Gtk::Label *l);
	void set_mode_menu(Gtk::Menu *m);
protected:

  // Events we implement
  // Note that we could use gtkmm's "signals and slots" mechanism
  // instead, but for many classes there's a convenient member
  // function one just needs to define that'll be called with the
  // event.

  // Called when GL is first initialized
  virtual void on_realize();
  // Called when our window needs to be redrawn
  virtual bool on_expose_event(GdkEventExpose* event);
  // Called when the window is resized
  virtual bool on_configure_event(GdkEventConfigure* event);
  // Called when a mouse button is pressed
  virtual bool on_button_press_event(GdkEventButton* event);
  // Called when a mouse button is released
  virtual bool on_button_release_event(GdkEventButton* event);
  // Called when the mouse moves
  virtual bool on_motion_notify_event(GdkEventMotion* event);

private:
	// private functions
	void initialize();
	void drawCube(Point3D *p);
	void drawModelGnomon(Point3D *p);
	void drawWorldGnomon(Point3D *p);
	void drawLine(Point3D p1, Point3D p2);
	Point2D setNormalized(Point3D p);
	Point2D mapViewport(Point2D p);
	void drawViewport();
	void setViewportCoordinates();
	void setViewportAttributes(Point2D p1, Point2D p2);
	void setLabelContext();
	bool nearPlaneClipping(Point3D *p1, Point3D *p2);
	bool otherPlanesClipping(Point3D *p1, Point3D *p2);
  // *** Fill me in ***
  // You will want to declare some more matrices here
  Matrix4x4 m_projection;
	Matrix4x4 m_model_scale;
	Matrix4x4 m_model_transrot;
	Matrix4x4 m_view;
	
	// drawing arrays
	Point3D m_cube[8];
	Point3D m_world_gnomon[4];
	Point3D m_model_gnomon[4];
	Point2D m_viewport[4];
	
	// flags
	Point2D init_point;
	Point2D init_viewport_point;
	Point2D final_viewport_point;
	bool button1, button2, button3;
	double p_near, p_far, p_FOV;
	double viewport_width, viewport_height;
	double viewport_x, viewport_y;
	Gtk::Label *m_label;
	
	// mode
	myMode m_mode;

	// mode_menu
	Gtk::Menu *mode_menu;
};

#endif
